/*
 *    Copyright 2009-2012 The 99 Software Foundation
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.nnsoft.guice.rocoto.variables;

import java.util.Map;

/**
 * Appender which relies on another appender to provides a configuration key, and a fallback appender in case no configuration value is found.
 * 
 * @since 6.0
 */
final class KeyAppender extends AbstractAppender
{
	/** Appender which will resolve key (add the possibility for dynamic variable) */
	private final Appender key;

	/** Appender which will resolve default value */
	private Appender defaultValue;

	/** Parser to use if dynamic resolution is needed */
	private Parser parser;

	/**
	 * Constructor for key without default value.
	 * 
	 * @param parser The parser from which this appender has been created.
	 * @param chunk
	 * @param key Appender to resolve configuration key.
	 */
	public KeyAppender( final Parser parser, final String chunk, final Appender key )
	{
		this(parser, chunk, key, null);
	}

	/**
	 * Constructor for key without default value.
	 * 
	 * @param parser The parser from which this appender has been created.
	 * @param chunk
	 * @param key Appender to resolve configuration key.
	 * @param defaultValue Appender to resolve default value, may be null.
	 */
	public KeyAppender( final Parser parser, final String chunk, final Appender key, final Appender defaultValue )
	{
		super(chunk);
		this.parser = parser;
		this.key = key;
		this.defaultValue = defaultValue;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void doAppend( StringBuilder buffer, Map<String, String> configuration, Tree<Appender> context )
	{

		// Resolve key eventually
		StringBuilder keyBuffer = new StringBuilder();
		key.append(keyBuffer, configuration, context);
		String resolvedKey = keyBuffer.toString();

		String resolvedValue = configuration.get(resolvedKey);
		if ( resolvedValue != null )
		{
			// Resolved value from the configuration may have variable
			// unresolved
			Resolver value = parser.parse(resolvedValue);
			if ( !value.needsResolving() )
			{
				buffer.append(resolvedValue);

			}
			// Process value
			else
			{
				if ( !(value instanceof Appender) )
				{
					resolvedValue = value.resolve(configuration);
				} else
				{
					StringBuilder resolvedValueBuffer = new StringBuilder();
					((Appender) value).append(resolvedValueBuffer, configuration, context);
					resolvedValue = resolvedValueBuffer.toString();
				}

				buffer.append(resolvedValue);
				// Update the configuration (not necessary, but can speed up a little when complex dependency between variables)
				configuration.put(resolvedKey, resolvedValue);
			}
		}
		// No value found from configuration, take default one
		else if ( defaultValue != null )
		{
			defaultValue.append(buffer, configuration, context);
		}
		// Fallback, print original chunk, will let the possibility to resolve
		// it later
		else
		{
			buffer.append(chunk);
		}
	}

	@Override
	public boolean equals( Object obj )
	{
		if ( obj == this )
		{
			return true;
		}
		if ( obj instanceof KeyAppender )
		{
			KeyAppender other = (KeyAppender) obj;
			return (key != null ? key.equals(other.key) : other.key == null)
					&& (defaultValue != null ? defaultValue.equals(other.defaultValue) : other.defaultValue == null);
		}
		return false;
	}

	@Override
	public int hashCode()
	{
		return (key != null ? key.hashCode() : 0) + (defaultValue != null ? defaultValue.hashCode() * 31 : 0);
	}

	/**
	 * @return Always true
	 */
	public boolean needsResolving()
	{
		return true;
	}
}
